﻿namespace FitnessTrackerUI.ViewModel
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Reactive.Concurrency;
    using System.Reactive.Linq;
    using System.Threading;
    using FitnessTracker.Entities;
    using FitnessTracker.Observables;
    using System.Linq;

    public class ConnectToHxMViewModel : IObserver<byte[]>, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private readonly FitnessTrackerEntities fitnessTrackerEntities = new FitnessTrackerEntities();
        private readonly byte numberOfSerialPacketsToReceiveToConfirmConnection;
        private string statusMessage;
        private string serialPortName;
        private IDisposable portSubscriptionCancellation;
        private byte numberOfValidPacketsReceived;
        private bool isReconnectionApplicable = true;
        private bool isConnectionValidated;

        public bool IsConnectionToHxMValidated
        {
            get
            {
                return isConnectionValidated;
            }
            set
            {
                isConnectionValidated = value;
                OnPropertyChanged("IsConnectionToHxMValidated");
            }
        }

        public bool IsReconnectionApplicable
        {
            get
            {
                return isReconnectionApplicable;
            }
            set
            {
                isReconnectionApplicable = value;
                this.OnPropertyChanged("IsReconnectionApplicable");
            }
        }

        public string StatusMessage
        {
            get
            {
                return statusMessage;
            }
            set
            {
                statusMessage = value;
                OnPropertyChanged("StatusMessage");
            }
        }

        public string SerialPortName
        {
            get
            {
                return serialPortName;
            }
            set
            {
                serialPortName = value;
                fitnessTrackerEntities.HxMConfigs.Single().comPort = SerialPortName;
                fitnessTrackerEntities.SaveChanges();
                OnPropertyChanged("SerialPortName");
            }
        }

        public ConnectToHxMViewModel()
        {
            if (!string.IsNullOrEmpty(fitnessTrackerEntities.HxMConfigs.Single().comPort))
            {
                SerialPortName = fitnessTrackerEntities.HxMConfigs.Single().comPort;
                this.numberOfSerialPacketsToReceiveToConfirmConnection =
                    (byte)fitnessTrackerEntities.HxMConfigs.Single().numOfPacketsRequiredToConfirmConnection;
            }
        }

        public void AttemptConnection()
        {
            var serialPortReader = SerialPortReader
                .GetInstance(this.SerialPortName)
                .Timeout(TimeSpan.FromSeconds(5))
                .Take(this.numberOfSerialPacketsToReceiveToConfirmConnection);

            this.portSubscriptionCancellation = serialPortReader
                .ObserveOn(SynchronizationContext.Current)
                .SubscribeOn(NewThreadScheduler.Default)
                .Subscribe(this);

            numberOfValidPacketsReceived = 0;
            StatusMessage = string.Empty;
            IsReconnectionApplicable = false;
            IsConnectionToHxMValidated = false;
        }

        public void OnNext(byte[] value)
        {
            Debug.WriteLine("ConnectToHxMviewModel received new packet");
            if (HxMReader.IsBufferValid(value))
            {
                StatusMessage += "Received Valid Data Point from HxM" + Environment.NewLine;
                this.numberOfValidPacketsReceived++;
            }
            else
            {
                StatusMessage += string.Format(
                    "Received invalid packet on Port {0} {1}", this.SerialPortName, Environment.NewLine);
            }

            Debug.WriteLine("Value of StatusMessage is now {0}", this.StatusMessage);
            Debug.WriteLine("Value of IsConnectionToHxMValidated is now {0}", this.IsConnectionToHxMValidated);
        }

        public void OnError(Exception error)
        {
            this.portSubscriptionCancellation.Dispose();
            StatusMessage +=
                string.Format("Error occured while opening or reading Port {0}, {1} {2}", this.SerialPortName, error.Message, Environment.NewLine);
            numberOfValidPacketsReceived = 0;
            IsConnectionToHxMValidated = false;
            IsReconnectionApplicable = true;
        }

        public void OnCompleted()
        {
            this.portSubscriptionCancellation.Dispose();

            if (this.numberOfValidPacketsReceived == this.numberOfSerialPacketsToReceiveToConfirmConnection)
            {
                StatusMessage +=
                string.Format("Connection to Port {0} validated {1}", this.SerialPortName, Environment.NewLine);
            }
            else
            {
                StatusMessage +=
                string.Format("Unable to establish connection to Port {0} {1}", this.SerialPortName, Environment.NewLine);
            }

            IsReconnectionApplicable = true;
            IsConnectionToHxMValidated = 
                (this.numberOfValidPacketsReceived == this.numberOfSerialPacketsToReceiveToConfirmConnection);
            Debug.WriteLine("Set IsConnectionToHxMValidated to {0} in OnCompleted", IsConnectionToHxMValidated);

            
        }

        private void OnPropertyChanged(string propertyName)
        {
            Debug.WriteLine("OnPropertyChangedEvent raised for {0}", propertyName);
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName)); 
            }
        }
    }
}
